#include "thread.h"
#include "stdint.h"
#include "string.h"
#include "global.h"
#include "debug.h"
#include "interrupt.h"
#include "print.h"
#include "process.h"
#include "file.h"
#include "stdio.h"

#define PG_SIZE 4096

struct task_struct *idle_thread;    // idle线程pcb
struct task_struct *main_thread;    //主线程PCB
struct list thread_ready_list;      //就绪队列
struct list thread_all_list;        //所有任务队列
struct lock pid_lock;               // pid锁
static struct list_elem *thread_tag; //用于保存队列中的线程节点

extern void switch_to(struct task_struct *cur, struct task_struct *next);

extern void init(void); // 定义在main.c中

/* 系统空闲时自动运行的线程 */
static void idle(void *arg UNUSED) {
    while(1) {
        thread_block(TASK_BLOCKED);
        // 执行hlt时必须保证处于开中断
        asm volatile("sti; hlt"
                     :
                     :
                     : "memory");
        // hlt让处理器停止执行指令,将处理器挂起,等待中断发生后再运行
    }
}

/* 获取当前线程PCB指针 */
struct task_struct* running_thread() {
    uint32_t esp;
    asm("mov %%esp, %0"
        : "=g"(esp));
    /* 取esp整数部分,即PCB起始地址 */
    return (struct task_struct *)(esp & 0xfffff000);
}

/* 分配Pid */
static pid_t allocate_pid(void) {
    static pid_t next_pid = 0;
    lock_acquire(&pid_lock);
    next_pid++;
    lock_release(&pid_lock);
    return next_pid;
}

/* 由 kernel_thread 去执行 function(func_arg) */
static void kernel_thread(thread_func *function, void *func_arg){
    /* 执行 function 前要开中断，避免后面的时钟中断被屏蔽，而无法调度其他线程 */
    intr_enable(); // 利用时钟中断,将cpu使用权移交给内核,内核调度器发放cpu使用权
    function(func_arg);
}

/* 初始化线程栈 thread_stack，将待执行的函数和参数放到 thread_stack 中相应的位置 */
void thread_create(struct task_struct *pthread, thread_func function, void *func_arg){
    /* 先预留中断使用栈的空间，可见 thread.h 中定义的结构 */
    pthread->self_kstack -= sizeof(struct intr_stack);

    /* 再留出线程栈空间，可见 thread.h 中定义 */
    pthread->self_kstack -= sizeof(struct thread_stack);
    struct thread_stack *kthread_stack = (struct thread_stack *)pthread->self_kstack;
    kthread_stack->eip = kernel_thread;
    kthread_stack->function = function;
    kthread_stack->func_arg = func_arg;
    kthread_stack->ebp = kthread_stack->ebx = kthread_stack->esi = kthread_stack->edi = 0;
}

/* 初始化线程基本信息 */
void init_thread(struct task_struct *pthread, char *name, int prio) {
    memset(pthread, 0, sizeof(*pthread));
    pthread->pid = allocate_pid();
    strcpy(pthread->name, name);

    if (pthread == main_thread) {
        /* 由于把 main 函数也封装成一个线程，并且它一直是运行的，故将其直接设为 TASK_RUNNING */
        pthread->status = TASK_RUNNING;
    } else {
        pthread->status = TASK_READY;
    }

    pthread->priority = prio;
    /* self_kstack 是线程自己在内核态下使用的栈顶地址 */
    pthread->self_kstack = (uint32_t *)((uint32_t)pthread + PG_SIZE);
    pthread->ticks = prio;
    pthread->elapsed_ticks = 0;
    pthread->pgdir = NULL;
    pthread->stack_magic = 0x19870916; // 自定义的魔数

    pthread->fd_table[0] = 0;
    pthread->fd_table[1] = 1;
    pthread->fd_table[2] = 2;

    pthread->cwd_inode_nr = 0; // 默认为根目录
    pthread->parent_pid = -1;   // 初始化父进程pid

    uint8_t fd_idx = 3;
    while (fd_idx < MAX_FILES_OPEN_PER_PROC) {
        pthread->fd_table[fd_idx] = -1;
        fd_idx++;
    }
}

/* 创建一优先级为 prio 的线程，线程名为 name，线程所执行的函数是 function(func_arg) */
struct task_struct *thread_start(char *name,
                                 int prio,
                                 thread_func function,
                                 void *func_arg) {
    /* pcb 都位于内核空间，包括用户进程的 pcb 也是在内核空间 */
    struct task_struct *thread = get_kernel_pages(1);

    init_thread(thread, name, prio);
    thread_create(thread, function, func_arg);

    enum intr_status old_status = intr_disable();
    /* 确保之前不在队列中 */
    ASSERT(!elem_find(&thread_ready_list, &thread->general_tag));
    /* 加入就绪线程队列 */
    list_append(&thread_ready_list, &thread->general_tag);

    /* 确保之前不在队列中 */
    ASSERT(!elem_find(&thread_all_list, &thread->all_list_tag));
    /* 加入就绪线程队列 */
    list_append(&thread_all_list, &thread->all_list_tag);

    intr_set_status(old_status);

    // asm volatile("movl %0, %%esp; pop %%ebp; pop %%ebx; pop %%edi; pop %%esi; ret"
    //              :
    //              : "g"(thread->self_kstack)
    //              : "memory");
    return thread;
}

/* 实现任务调度 */
void schedule(void){
    ASSERT(intr_get_status() == INTR_OFF);  // 中断应该处于关闭状态
    struct task_struct *cur = running_thread();
    if (cur->status == TASK_RUNNING) {
        // 该线程时间片到了,被换下cpu,将其加入到就绪队列尾
        ASSERT(!elem_find(&thread_ready_list, &cur->general_tag));  // 此线程不应该在就绪列表内
        list_append(&thread_ready_list, &cur->general_tag);         // 加入到就绪列表尾
        cur->ticks = cur->priority;                                 // 重置ticks
        cur->status = TASK_READY;
    } else {
        // 不是因为时间片到了,该线程需要某件事情发生后才能上cpu运行,无需加入队列
    }

    /* 如果就绪队列中没有可运行的任务,就唤醒idle */
    if (list_empty(&thread_ready_list)) {
        thread_unblock(idle_thread);
    }
    
    ASSERT(!list_empty(&thread_ready_list));        // 就绪列表不应为空(在没有idle线程时的暂时解决)
    thread_tag = NULL;                              // thread_tag清空
    thread_tag = list_pop(&thread_ready_list);      // 将就绪队列的第一个就绪线程弹出,准备上cpu
    struct task_struct *next = elem2entry(struct task_struct, general_tag, thread_tag);
    next->status = TASK_RUNNING;

    // put_str(next->name);
    // put_str(": ");
    // put_int(next->pgdir);
    // put_str(" ");
    // put_int((uint32_t)next->userprog_vaddr.vaddr_start);
    // put_str(" ");
    // put_str("\n");

    /* 激活任务页表等 */
    process_activate(next);

    switch_to(cur, next);
}

static void make_main_thread(void) {
    /** 因为 main 线程早已运行，
     *  咱们在 loader.S 中进入内核时的 mov esp,0xc009f000，
     *  就是为其预留 pcb 的，因此 pcb 地址为 0xc009e000，
     *  不需要通过 get_kernel_page 另分配一页*/
    main_thread = running_thread();
    init_thread(main_thread, "main", 31);

    /** main 函数是当前线程，当前线程不在 thread_ready_list 中，
     * 所以只将其加在 thread_all_list 中 */
    ASSERT(!elem_find(&thread_all_list, &main_thread->all_list_tag));
    list_append(&thread_all_list, &main_thread->all_list_tag);
}

/* 当前线程将自己阻塞,标志其状态为stat */
void thread_block(enum task_status stat) {
    /* stat 取值为TASK_BLOCKED,TASK_WAITING,TASK_HANGING,也就是只有这三种状态才不会被调度 */
    ASSERT(((stat == TASK_BLOCKED) || (stat == TASK_WAITING) || (stat == TASK_HANGING)));
    enum intr_status old_status = intr_disable();
    struct task_struct *cur_thread = running_thread();
    cur_thread->status = stat;
    schedule(); // 开始阻塞
    // 解除阻塞后再往后运行
    intr_set_status(old_status);
}

/* 将线程pthread解除阻塞 */
void thread_unblock(struct task_struct *pthread) {
    enum intr_status old_status = intr_disable();
    ASSERT(((pthread->status == TASK_BLOCKED) || (pthread->status == TASK_WAITING) || (pthread->status == TASK_HANGING)));
    if (pthread->status != TASK_READY) {
        ASSERT(!elem_find(&thread_ready_list, &pthread->general_tag));
        if (elem_find(&thread_ready_list, &pthread->general_tag)) {
            PANIC("thread_unblock: blocked thread in ready_list\n");
        }
        list_push(&thread_ready_list, &pthread->general_tag); // 放在列表队首,尽快调度
        pthread->status = TASK_READY;
    }
    intr_set_status(old_status);
}

/* 主动让出cpu */
void thread_yield(void) {
    struct task_struct *cur = running_thread();
    enum intr_status old_status = intr_disable();
    ASSERT(!elem_find(&thread_ready_list, &cur->general_tag));
    list_append(&thread_ready_list, &cur->general_tag);
    cur->status = TASK_READY;
    schedule();
    intr_set_status(old_status);
}

/* fork进程时获取pid */
int16_t fork_pid(void) {
    return allocate_pid();
}

/* 以填充空格的方式输出buf */
static void pad_print(char *buf, int32_t buf_len, void *ptr, char format)
{
    memset(buf, 0, buf_len);
    uint8_t out_pad_0idx = 0;
    switch (format)
    {
    case 's':
        out_pad_0idx = sprintf(buf, "%s", ptr);
        break;
    case 'd':
        out_pad_0idx = sprintf(buf, "%d", *((int16_t *)ptr));
    case 'x':
        out_pad_0idx = sprintf(buf, "%x", *((uint32_t *)ptr));
    }
    while (out_pad_0idx < buf_len)
    { // 以空格填充
        buf[out_pad_0idx] = ' ';
        out_pad_0idx++;
    }
    sys_write(stdout_no, buf, buf_len - 1);
}

/* 用于在list_traversal函数中的回调函数,用于针对线程队列的处理 */
static bool elem2thread_info(struct list_elem *pelem, int arg UNUSED)
{
    struct task_struct *pthread = elem2entry(struct task_struct, all_list_tag, pelem);
    char out_pad[16] = {0};

    pad_print(out_pad, 16, &pthread->pid, 'd');

    if (pthread->parent_pid == -1)
    {
        pad_print(out_pad, 16, "NULL", 's');
    }
    else
    {
        pad_print(out_pad, 16, &pthread->parent_pid, 'd');
    }

    switch (pthread->status)
    {
    case 0:
        pad_print(out_pad, 16, "RUNNING", 's');
        break;
    case 1:
        pad_print(out_pad, 16, "READY", 's');
        break;
    case 2:
        pad_print(out_pad, 16, "BLOCKED", 's');
        break;
    case 3:
        pad_print(out_pad, 16, "WAITING", 's');
        break;
    case 4:
        pad_print(out_pad, 16, "HANGING", 's');
        break;
    case 5:
        pad_print(out_pad, 16, "DIED", 's');
    }
    pad_print(out_pad, 16, &pthread->elapsed_ticks, 'x');

    memset(out_pad, 0, 16);
    ASSERT(strlen(pthread->name) < 17);
    memcpy(out_pad, pthread->name, strlen(pthread->name));
    strcat(out_pad, "\n");
    sys_write(stdout_no, out_pad, strlen(out_pad));
    return false; // 此处返回false是为了迎合主调函数list_traversal,只有回调函数返回false时才会继续调用此函数
}

/* 打印任务列表 */
void sys_ps(void)
{
    char *ps_title = "PID            PPID           STAT           TICKS          COMMAND\n";
    sys_write(stdout_no, ps_title, strlen(ps_title));
    list_traversal(&thread_all_list, elem2thread_info, 0);
}

/* 初始化线程环境 */
void thread_init(void) {
    put_str("thread_init start\n");
    list_init(&thread_ready_list);
    list_init(&thread_all_list);
    lock_init(&pid_lock);
    
    // 先创建init进程
    // 创建为用户进程
    process_execute(init, "init"); // pid应该为1
    
    

    /* 将main函数创建为线程 */
    make_main_thread();
    /* 创建 idle 线程 */
    idle_thread = thread_start("idle", 10, idle, NULL);
    put_str("thread_init done\n");
}
